
import logging
log = logging.getLogger('npc_sprite')
log.debug("imported") 

import pygame

import cocos
from cocos.director import director
from cocos.sprite import Sprite
from cocos.actions import *
import pyglet

log.debug("lib imports finished")

import anims
from filedef import NPC
import util
import extras
import game_audio
from force import Force
from pyknic.geometry import Vec2
import run_cfg_game as cfg
from force import Force


log.debug("imports finished")
 
class TestMapCollider(cocos.tiles.RectMapCollider):
    "Stops this sprite from moving into a tile"
    def collide_bottom(self, dy):
        print "collide_bottom", dy

    def collide_left(self, dx):
        print "collide_left",dx

    def collide_right(self, dx):
        print "collide_right",dx

    def collide_top(self, dy):
        print "collide_top",dy

class NPCSprite(Sprite, TestMapCollider):
    def __init__(self, fname, pos, speach_offset = None, collision_data =  None, states =  None, name = "NPC", force = None, anc=(0,0)):
        super(NPCSprite, self).__init__(fname, position = pos, anchor = anc)
        log.debug( "__init__:"+str((fname, pos, speach_offset, collision_data, states, name)))
        
        self.tasks = None
        self.speach = None
        self.name = name
        if speach_offset is None:
            speach_offset = [0,0]
        self.speach_offset = speach_offset
        self.forces = []
        self.force  = force               
        
        if states is not None:
            self.states = util.Enumerate(states)
            
        self.collision_data = collision_data
        
        self.layer = 0
        self.dx, self.dy = [0,0]
        self.map = None
        self.old_rect = self.get_rect()
        self.destroyed = False
        self.disabled = False

         
    def on_enter(self):
        super(NPCSprite, self).on_enter()        
        self.schedule_interval(self.update_visibility, 2)
        
    def on_exit(self):
        self.unschedule(self.update_visibility)
        super(NPCSprite, self).on_exit()
        
    def execute(self, data):
        for type, vals in data.items():
            getattr(self, type)(vals)
    
    def speak(self, speaklist):
        log.debug( "Speak:"+str(speaklist))
        tasks = ""
        for task,sec in speaklist:
            tasks += "CallFunc(self.say,'"+ task+"')+ Delay("+str(sec)+") +"
        tasks = tasks.rstrip("+")        
        self.do( eval(tasks) ) 
        
    def say(self, text):
        if self.speach is None:
            self.speach = cocos.text.Label(text, anchor_x='center', anchor_y='center')
            self.speach.position = self.speach_offset
            self.add(self.speach)
        else:
            self.speach.element.text = text

        
    def action(self, actionslist):
        log.debug( "Actions:"+str(actionslist))
        tasks = ""
        if actionslist is not None:
            for task in actionslist:
                tasks += task+"+"
            tasks = tasks.rstrip("+")        
            self.do( eval(tasks) )

    def add_force(self,force):
        self.forces.append(force)

    def collision_action(self, action_list, npc1 = None, npc2 = None):
        log.debug( "collision_action:"+str(action_list))

        if action_list is not None:
            for task in action_list.keys():
                args = action_list[task]
                string = "self."+str(task)+"("+str(args)+")"
                log.debug( "action:"+str(task)+" args:"+str(args)+" eval:"+string)
                eval(string)
                #self.do( CallFunc(eval("self."+task, args) ))


    def enable(self, disabled = False):
        log.info("Enable "+str(id(self)))
        self.disabled = False
        
    def disable(self, time):
        log.info("Disabe "+str(id(self)))
        self.disabled = True        
        if time>0:
            log.info("Setting delay of "+str(time)+" seconds before reenable")
            self.do(Delay(time)+CallFunc(self.enable, False))
            
    def destroy(self):
        # HORRID HACK!
        if self.destroyed is False:
            self.destroyed = True
            log.debug("trying to remove myself ("+str(id(self))+") from parent ("+str(id(self.parent))+")")
            self.parent.remove(self)
            #log.debug("imports Level")

            from level import Level

            if self.parent.parent.parent.__class__ is Level:
                log.debug("trying to remove myself ("+str(id(self))+") from ALL_MOBS ("+str(id(self.parent.parent.parent))+") LEVEL")
                self.parent.parent.parent.ALL_MOBS.remove(self)
                if(self.force is not None):
                    self.force.active=False
                    
        
                
    def update_visibility(self, dt):
        func = getattr(self.parent, "get_view", None)
        if func is None:
            return
        
        view = self.parent.get_view()
        if (self.position[0] < view[0] -64) or (self.position[0]-view[0] > (view[2]+64)) or \
                (self.position[1] < view[1] -64) or (self.position[1]-view[1] > (view[3]+64)):
            
            if self.visible:
                self.visible = False
                log.info(( "Setting vis:", self.position, self.visible, view ))

        else:
            if self.visible is False:
                self.visible = True
                log.info(( "Setting vis:", self.position, self.visible, view ))
    
    def update(self, dt):
        pass    

    # WAHHH Tickery!
    def _get_rect(self):
        r = self.get_rect()
        return pygame.Rect(r.left, r.top, r.width, r.height)
    rect = property(_get_rect)

    def check_collide(self, map = None):
        if map is None:
            map = self.map
        
        if map is not None:    
            last = self.old_rect
            new = self.get_rect()
            self.old_rect = new
            self.collide_map(map, last, new, self.dy, self.dx)
                
    def collision_response(self, *args):
        #print 'response', args
        
        return self.collide()
        
    def collide(self):
        if self.disabled:
            log.error("Disabled! Why am I colliding?"+str(id(self)))
            return
        
        # if we collide - what should we do?
        ret = {}
        if self.collision_data is not None:
            for item in self.collision_data:
                action, value = item
    
                if action == "destroy":
                    
                    if value[0] == True:
                        self.destroy()
                    
                    # play sound
                    if len(value) >1:
                        game_audio.play_sound(value[1])
                elif action == "disable":
                    self.disable(float(value[0]))
                    # play sound
                    if len(value) >1:
                        game_audio.play_sound(value[1])                                      
                else:
                    
                    ret[action] = value
            
        return ret
        
        
def CreateNPCSprite(table, id, position = None): 
    
    obj = table.get(id, None)
    if obj is None:
        log.error( "ERROR cant get object ["+str(id)+"]")
        return None
    
    if position is None:
        position = obj.position
        
    rotation  = getattr(obj, "rotation", 0.0)    
    scale  = getattr(obj, "scale", 1.0)    
    pos_offset = getattr(obj, "pos_offset", (0,0))
    anchor = getattr(obj, "anchor", (0,0))
        
    name = getattr(obj, "name", "NPC")
    speach_offset = getattr(obj, "speach_offset", None)
    collision_data = getattr(obj, "collide", None)
    states = getattr(obj, "states", None)
    animation = getattr(obj, "animation", None)
    tilemapidx = getattr(obj, "tilemapidx", None)
    actions = getattr(obj, "actions", None)
    dim = getattr(obj, "dim", None)
    force = getattr(obj, "force", None)

    position  = pos_offset[0]+position[0], pos_offset[1]+position[1]

    if(force is not None):
        force = Force(position, force)
    
    log.debug( "CreateNPCSprite:"+obj.name+" " +str(position)+" " +str(dim))
    
    if animation:
        res = anims.GetAnimSeqFromFile(obj.path, dim, animation[0], animation[1])
            
        s = NPCSprite(res, position, speach_offset, collision_data, states, name,force, anchor)
        log.debug( "Animated")
    else: 
        if tilemapidx: #no frames - this is part of a tile map
            
            res = anims.GetFramesFromFile(obj.path, dim)
            
            log.debug( "CreateNPCSprite:"+str(obj.tilemapidx)+" frames available:" +str(len(res)))

            s = NPCSprite(res[obj.tilemapidx], position, speach_offset, collision_data, states, name, force, anchor)
            log.debug( "Single frame")
            
        else:  #no frames and not a tile - the whole file is this sprite
            s = NPCSprite(obj.path, position, speach_offset, collision_data, states, name, force, anchor)
            log.debug( "whole file")
        
    if actions:
        if obj.actions is not None: 
            s.execute(obj.actions)
            
    s.layer = getattr(obj, "layer", 0)
    s._set_rotation(float(rotation))
    s._set_scale(float(scale))
    
    extra,zorder = extras.CreateExtra(getattr(obj, "extra", None))
    #if extra is not None:
    #    s.add(extra, z= zorder)
                   
    return s, extra
          
def CreateMobsFromLevel(level_data, mobs_table, scale):
    width , height, tile_data = level_data    
    fg_npcs = []
    bg_npcs = []
    extras = []
    plr_start = None
    for x in xrange(width):
        for y in xrange(height):            
            fg_tile, bg_tile, action = tile_data[y][x]
            if action > 0:
                pos = x*scale[0],y*scale[1]
                if action == 16:
                    plr_start = pos
                    
                else:               
                    tile = action
                        
                    npc,extra  = CreateNPCSprite(mobs_table, tile, pos)
                    if npc is not None:
                        if (npc.layer == 0):
                            fg_npcs.append(npc)
                        else:
                            bg_npcs.append(npc)
                    if extra is not None:
                        extra.position = pos
                        extra.rotation = npc.rotation
                        extras.append(extra)

                    
    return bg_npcs, fg_npcs, plr_start, extras
    
def run():
    from lookup import BuildLookupTable,SortByName, SortByTile
    
    table = BuildLookupTable("mob", "defs/testnpc.def") 
    npcs = SortByName(table)
    #tiles = SortByTile(table)
    animated_sprite = CreateNPCSprite(npcs, "test1")
    tile_sprite = CreateNPCSprite(npcs, "test two")
    
    main_scene = cocos.scene.Scene()
    
    main_scene.add(animated_sprite)
    main_scene.add(tile_sprite)
    
    return main_scene

if __name__ == '__main__':



    director.init(width=640, height=480, do_not_scale=True)    
    director.run(run())
  
    